package dao;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

// 通过这个类来封装针对 file_meta 表进行操作
public class FileDao {
    // 1. 初始化数据库 (建表)
    public void initDB() {
        // 读取sql语句
        // 根据sql 执行
        Connection connection = null;
        Statement statement = null;
        try {
            connection = DBUtil.getConnection();
            statement = connection.createStatement();
            String[] sqls = getInitSQL();
            for (String sql : sqls) {
                System.out.println("[initDB] sql: " + sql);
                statement.executeUpdate(sql);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,null);
        }
    }

    // 从db.sql 中读取文件内容
    private String[] getInitSQL() {
        // 用这个stringBuilder 来存储最后结果
        StringBuilder stringBuilder = new StringBuilder();
        // 动态获取到db.sql文件的路径 此处不要是绝对路径
        try (InputStream inputStream = FileDao.class.getClassLoader().getResourceAsStream("db.sql")) {
            try(InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf8")) {
                while (true) {
                    // 此处虽然用int 但实际上是char
                    int ch = inputStreamReader.read();
                    if(ch == -1) {
                        // 文件读取完毕
                        break;
                    }
                    stringBuilder.append((char)ch);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.toString().split(";");
    }

    // 2. 插入文件/目录数据到数据库
    //    这里提供一个“批量插入”的方法
    public void add(List<FileMeta> fileMetas) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = DBUtil.getConnection();
            // 关闭连接的自动提交功能
            connection.setAutoCommit(false);
            String sql = "insert into file_meta values(null,?,?,?,?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            for (FileMeta fileMeta : fileMetas) {
                // 针对当前 FileMeta 对象 替换成sql语句中 把 ？ 替换成具体数值
                preparedStatement.setString(1,fileMeta.getName());
                preparedStatement.setString(2,fileMeta.getPath());
                preparedStatement.setBoolean(3,fileMeta.isDirectory());
                preparedStatement.setString(4,fileMeta.getPinyin());
                preparedStatement.setString(5,fileMeta.getPinyinFirst());
                preparedStatement.setLong(6,fileMeta.getSize());
                preparedStatement.setTimestamp(7,new Timestamp(fileMeta.getLastModified()));

                // 使用 addBatch 把这个构造好的片段 累计起来
                preparedStatement.addBatch();

                System.out.println("[insert] 插入文件: " + fileMeta.getPath() + File.separator + fileMeta.getName());
            }
            // 執行所有 sql 片段
            preparedStatement.executeBatch();
            // 執行完畢之後 通過 commit 告知數據庫
            connection.commit();
        } catch (SQLException throwables) {
            // 回滾
            try {
                if(connection != null) {
                    connection.rollback();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } finally {
            DBUtil.close(connection,preparedStatement,null);
        }
    }

    // 3. 按照特定关键词进行查询
    //    这个是实现文件搜索功能的时候，必备的部分
    //    此处搜索的pattern 可能是文件名字一部分，也可能是文件名拼音一部分，也可能是首字母
    public List<FileMeta> searchByPattern(String pattern) {
        List<FileMeta> fileMetas = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select name, path, is_directory, size, last_modified from file_meta"
                    + " where name like ? or pinyin like ? or pinyin_first like ?"
                    + " order by path, name";
            statement = connection.prepareStatement(sql);
            statement.setString(1,"%" + pattern + "%");
            statement.setString(2,"%" + pattern + "%");
            statement.setString(3,"%" + pattern + "%");
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                String name = resultSet.getString("name");
                String path = resultSet.getString("path");
                boolean isDirectory = resultSet.getBoolean("is_directory");
                long size = resultSet.getLong("size");
                Timestamp lastModified = resultSet.getTimestamp("last_modified");
                FileMeta fileMeta = new FileMeta(name,path,isDirectory,size,lastModified.getTime());
                fileMetas.add(fileMeta);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return fileMetas;
    }

    // 4. 给定路径， 查询这个路径对应的结果，（这个路径下都有哪些文件）
    //    这个方法，会在后续重新扫描，更新数据库的时候用到
    public List<FileMeta> searchByPath(String targetPath) {
        List<FileMeta> fileMetas = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select name, path, is_directory, size, last_modified from file_meta"
                    + " where path = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,targetPath);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                String name = resultSet.getString("name");
                String path = resultSet.getString("path");
                boolean isDirectory = resultSet.getBoolean("is_directory");
                long size = resultSet.getLong("size");
                Timestamp lastModified = resultSet.getTimestamp("last_modified");
                FileMeta fileMeta = new FileMeta(name,path,isDirectory,size,lastModified.getTime());
                fileMetas.add(fileMeta);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return fileMetas;
    }

    // 5. 删除数据
    //    发现某个文件已经从磁盘上删掉了，此时就需要把表里的内容进行更新
    //    进行删除的时候可能是普通文件-> 直接删  也有可能删除的是目录 -> 目录里面的子文件和子目录都要删除掉
    public void delete(List<FileMeta> fileMetas) {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = DBUtil.getConnection();
            connection.setAutoCommit(false);
            // 此处构造的sql 要根据删除的内容区别对待
            for (FileMeta fileMeta : fileMetas) {
                String sql = null;
                if(!fileMeta.isDirectory()) {
                    // 普通文件
                    sql = "delete from file_meta where name = ? and path = ?";
                } else {
                    // 目录
                    // 例如要删除 D:/test
                    // 此处的path like ？ 要替换成 D:/test% 目的是吧这个文件开头的都要删除
                    sql = "delete from file_meta where (name = ? and path = ?) or (path like ?)";
                }
                statement = connection.prepareStatement(sql);
                if(!fileMeta.isDirectory()) {
                    // 普通文件
                    statement.setString(1,fileMeta.getName());
                    statement.setString(2,fileMeta.getPath());
                } else {
                    // 针对目录
                    statement.setString(1,fileMeta.getName());
                    statement.setString(2,fileMeta.getPath());
                    statement.setString(3,fileMeta.getPath() + File.separator + fileMeta.getName() + File.separator + "%");
                }
                statement.executeUpdate();
                System.out.println("[delete]" + fileMeta.getPath() + fileMeta.getName());
                // 此处是有多个statement 对象 所以要关闭多次
                statement.close();
            }
            // 告诉数据库执行完毕
            connection.commit();
        } catch (SQLException throwables) {
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } finally {
            DBUtil.close(connection,null,null);
        }
    }
}
